package com.gap.dam.module.business.search;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gap.dam.common.exception.ElasticSearchException;
import com.gap.dam.config.ElasticSearchProperties;
import com.gap.dam.module.business.search.domain.dto.AssetSearchDTO;
import com.gap.dam.util.PageUtil;
import com.gap.dam.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.WildcardQuery;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.HttpAsyncResponseConsumerFactory;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ValueCountAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;

import javax.annotation.Resource;
import javax.management.Query;
import java.io.IOException;


@Slf4j
public class BaseElasticSearchService {

    @Resource
    protected RestHighLevelClient client;

    @Resource
    private ElasticSearchProperties elasticSearchProperties;

    protected static final RequestOptions COMMON_REQUEST_OPTIONS;

    private static final int MAX_VALUE = 60;

    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        // 缓冲区设置为100M
        builder.setHttpAsyncResponseConsumerFactory(new HttpAsyncResponseConsumerFactory.HeapBufferedResponseConsumerFactory(100 * 1024));
        COMMON_REQUEST_OPTIONS = builder.build();


    }

    /**
     * 创建索引
     * @param index
     */
    protected void createIndexRequest(String index) {
        CreateIndexRequest request = new CreateIndexRequest(index);
        request.settings(Settings.builder()
                .put("index.number_of_shards", elasticSearchProperties.getIndex().getNumberOfShards().byteValue())
                .put("index.number_of_replicas", elasticSearchProperties.getIndex().getNumberOfReplicas()));
        try {
            XContentBuilder builder = JsonXContent.contentBuilder()
                    .startObject()
                        .startObject("mappings")
                            .startObject("doc")
                                .startObject("properties")
                                    .startObject("name")
                                        .field("type", "text")
                                    .endObject()
                                    .startObject("url")
                                        .field("type", "keyword")
                                    .endObject()
                                    .startObject("tags")
                                        .field("type", "keyword")
                                    .endObject()
                                    .startObject("updateTime")
                                        .field("type", "date")
                                    .endObject()
                                    .startObject("createTime")
                                        .field("type", "date")
                                    .endObject()
                                    .startObject("category")
                                        .field("type", "keyword")
                                    .endObject()
                                .endObject()
                            .endObject()
                        .endObject()
                    .endObject();
            request.source(builder);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            CreateIndexResponse createIndexResponse = client.indices().create(request, COMMON_REQUEST_OPTIONS);
            log.info("createIndex-isAcknowledged:{}", createIndexResponse.isAcknowledged());
            log.info("createIndex-{}", createIndexResponse.isShardsAcknowledged());
        } catch (IOException e) {
           throw new ElasticSearchException("Create index {" + index + "} failure");
        }
    }

    /**
     * 删除索引
     * @param index
     */
    protected void deleteIndexRequest(String index) {
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
        try {
            this.client.indices().delete(deleteIndexRequest, COMMON_REQUEST_OPTIONS);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new ElasticSearchException("Delete index {" + index + "} failure");
        }
    }

    protected void updateIndexReuqest(String index, String id, Object object) {
        try {
            UpdateRequest updateRequest = new UpdateRequest(index, id).doc(BeanUtil.beanToMap(object), XContentType.JSON);
            client.update(updateRequest, COMMON_REQUEST_OPTIONS);
        } catch (Exception ex) {
            throw new ElasticSearchException("Update index {" + index + "} failure");
        }
    }

    protected void deleteIndexRequest(String index, String id) {
        try {
            DeleteRequest deleteRequest = new DeleteRequest(index, id);
            client.delete(deleteRequest, COMMON_REQUEST_OPTIONS);
        } catch (Exception ex) {
            throw new ElasticSearchException("Delete index {" + index + "} failure");
        }
    }

    protected SearchResponse search(String index) {
        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, COMMON_REQUEST_OPTIONS);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new ElasticSearchException("Search index {" + index + "}失败");
        }
        return searchResponse;
    }

    protected SearchResponse searchByPage(String index, AssetSearchDTO assetSearchDTO) {
        Page page = PageUtil.convert2QueryPage(assetSearchDTO);

        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from((int)page.getCurrent());
        searchSourceBuilder.size((int)page.getSize());

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 传入了Name查询条件
        if (StringUtil.isNotEmpty(assetSearchDTO.getName())) {
            MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", assetSearchDTO.getName());
            boolQueryBuilder = boolQueryBuilder.must(matchQueryBuilder);
        }
        // 传入了Tags查询条件
        if (assetSearchDTO.getTags() != null) {
            for (String tag : assetSearchDTO.getTags()) {
                MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("tags", tag);

                boolQueryBuilder = boolQueryBuilder.must(matchQueryBuilder);
            }
        }
        // 传入了category查询条件
        if (StringUtil.isNotEmpty(assetSearchDTO.getCategory())) {
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("categoryId", assetSearchDTO.getCategory());
            boolQueryBuilder = boolQueryBuilder.must(termQueryBuilder);
        }
        buildGroupByBuilder(searchSourceBuilder);

        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        System.out.println(JSON.toJSONString(searchRequest.toString()));

        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, COMMON_REQUEST_OPTIONS);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new ElasticSearchException("Search index {" + index + "}失败");
        }
        return searchResponse;
    }

    /**
     * 根据CategoryId分页查询
     * @param index
     * @param assetSearchDTO
     * @return
     */
    protected SearchResponse searchPageByCategoryId(String index, AssetSearchDTO assetSearchDTO) {
        Page page = PageUtil.convert2QueryPage(assetSearchDTO);

        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from((int)page.getCurrent());
        searchSourceBuilder.size((int)page.getSize());

        if (StringUtils.isNotEmpty(assetSearchDTO.getCategory())) {
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("parentCategoryId", assetSearchDTO.getCategory());
            searchSourceBuilder.query(termQueryBuilder);
        }
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, COMMON_REQUEST_OPTIONS);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new ElasticSearchException("Search index {" + index + "}失败");
        }
        return searchResponse;
    }

    /**
     * 根据多个条件查询Assets，查询条件包括：name,tag,category
     * @param index
     * @param assetSearchDTO
     * @return
     */
    protected SearchResponse searchPageByMultiCondition(String index, AssetSearchDTO assetSearchDTO) {
        Page page = PageUtil.convert2QueryPage(assetSearchDTO);

        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from((int)page.getCurrent());
        searchSourceBuilder.size((int)page.getSize());

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 如果传入Category查询条件
        if (StringUtils.isNotEmpty(assetSearchDTO.getCategory())
                && StringUtils.isEmpty(assetSearchDTO.getName())
                && (assetSearchDTO.getTags() == null || assetSearchDTO.getTags().size() == 0)) {
            TermQueryBuilder parentCategoryIdTermQueryBuilder = QueryBuilders.termQuery("parentCategoryIdList", assetSearchDTO.getCategory());
            BoolQueryBuilder notCategoryBoolBuilder = QueryBuilders.boolQuery();
            TermQueryBuilder fileTypeTermQueryBuilder = QueryBuilders.termQuery("fileType", "category");
            notCategoryBoolBuilder.mustNot(fileTypeTermQueryBuilder);

            boolQueryBuilder.must(parentCategoryIdTermQueryBuilder)
                    .must(notCategoryBoolBuilder);
        }
        // 传入了Name查询条件
        if (StringUtil.isNotEmpty(assetSearchDTO.getName())) {
            BoolQueryBuilder boolQueryBuilder1 = QueryBuilders.boolQuery();
            WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("name", "*" + assetSearchDTO.getName().toLowerCase() + "*");
            boolQueryBuilder1.should(wildcardQueryBuilder);

            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("tags", assetSearchDTO.getName());
            boolQueryBuilder1.should(termQueryBuilder);

            boolQueryBuilder.must(boolQueryBuilder1);
        }
        // 传入了Tags查询条件
        if (assetSearchDTO.getTags() != null) {
            for (String tag : assetSearchDTO.getTags()) {
                TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("tags", tag);
                boolQueryBuilder.must(termQueryBuilder);
            }
        }

        // 创建聚合查询
        buildGroupByBuilder(searchSourceBuilder);

        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        System.out.println(JSON.toJSONString(searchRequest.toString()));

        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, COMMON_REQUEST_OPTIONS);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new ElasticSearchException("Search index {" + index + "}失败");
        }
        return searchResponse;
    }

    protected static IndexRequest buildIndexRequest(String index, String id, Object object) {
        return new IndexRequest(index).id(id).source(BeanUtil.beanToMap(object), XContentType.JSON);
    }

    protected static void buildGroupByBuilder(SearchSourceBuilder searchSourceBuilder) {
        TermsAggregationBuilder fieldBuilder = AggregationBuilders.terms("group_by_tags").field("tags");
        ValueCountAggregationBuilder countField = AggregationBuilders.count("count_value").field("tags");
        fieldBuilder.subAggregation(countField);
        searchSourceBuilder.aggregation(fieldBuilder);
    }
}
